package com.tbit.uqbike.service.base.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tbit.uqbike.constant.enums.AccountDayStatEnum;
import com.tbit.uqbike.constant.enums.AccountSumStatEnum;
import com.tbit.uqbike.enums.SystemCodeEnum;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.pojo.Account;
import com.tbit.uqbike.object.pojo.AccountDayStat;
import com.tbit.uqbike.object.pojo.AccountSumStat;
import com.tbit.uqbike.object.pojo.vo.ScreenLineChartSeriesVO;
import com.tbit.uqbike.object.pojo.vo.ScreenLineChartVO;
import com.tbit.uqbike.object.pojo.vo.ScreenPieVO;
import com.tbit.uqbike.object.value.LoginInfo;
import com.tbit.uqbike.object.view.NameObject;
import com.tbit.uqbike.service.base.ScreenService;
import com.tbit.uqbike.service.rpcservice.StatService;
import com.tbit.uqbike.webmanager.dao.core.AccountDao;
import com.tbit.uqbike.webmanager.dao.stat.AccountDayStatDao;
import com.tbit.uqbike.webmanager.dao.stat.AccountSumStatDao;
import com.tbit.utils.DateTimeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * sugar大屏
 *
 * @Author:BUGTian
 * @DATE: 2022/12/7
 */
@Service
public class ScreenServiceImpl implements ScreenService {
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private AccountSumStatDao accountSumStatDao;
    @Autowired
    private StatService statService;
    @Autowired
    private AccountDayStatDao accountDayStatDao;

    /**
     * yyyyMMdd格式转yyyy-MM-dd格式
     *
     * @param time
     * @return
     */
    private static String reparseTime(Integer time) {
        if (time == null) {
            return null;
        }
        String s = time.toString();
        if (s.length() != 8) {
            return null;
        }
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            stringBuilder.append(s.charAt(i));
            if (i == 3 || i == 5) {
                stringBuilder.append("-");
            }
        }
        return stringBuilder.toString();
    }

    @Override
    public List<ScreenPieVO> userDistribution(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        checkLogin(login);
        return accountSumStatDao.userDistribution(accountIdList, brandId);
    }

    @Override
    public Long singleResult(LoginInfo login, List<Integer> accountIdList, Integer dataType, int flag, Integer brandId) {
        checkLogin(login);
        switch (flag) {
            case SUM:
                return accountSumStatDao.singleSumResult(accountIdList, dataType, brandId);
            case CNT:
                return accountSumStatDao.singleCntResult(accountIdList, dataType, brandId);
            default:
                return 0L;
        }
    }

    @Override
    public Long singleResultToAccountDayStat(LoginInfo login, List<Integer> accountIdList, Integer dataType, int flag, Integer brandId, Integer startDataDate, Integer endDateDate) {
        checkLogin(login);
        switch (flag) {
            case SUM:
                return accountSumStatDao.singleSumResultToAccountDayStat(accountIdList, dataType, brandId, startDataDate, endDateDate);
            case CNT:
                return accountSumStatDao.singleCntResultToAccountDayStat(accountIdList, dataType, brandId, startDataDate, endDateDate);
            default:
                return 0L;
        }
    }

    @Override
    public List<ScreenPieVO> orderCondition(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        checkLogin(login);
        return accountSumStatDao.orderCondition(accountIdList, brandId);
    }

    @Override
    public List<ScreenPieVO> transactionData(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        checkLogin(login);
        return accountSumStatDao.transactionData(accountIdList, brandId);
    }

    @Override
    public Double yesterdaySameRatio(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        //获取昨日时间和上一年度同一天的时间
        Integer xStart = parseTime(DateUtil.yesterday());
        Integer yStart = parseTime(DateUtil.offset(DateUtil.yesterday(), DateField.YEAR, -1));
        //当天交易的开始结束时间为同一天
        return getRatio(AccountDayStatEnum.REVENUE_FROM_MERCHANT_PLATFORM,  accountIdList, login, brandId, xStart, xStart, yStart, yStart);
    }

    @Override
    public Double yesterdayRingRatio(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        //获取昨日时间和前天的时间
        Integer xStart = parseTime(DateUtil.yesterday());
        Integer yStart = parseTime(DateUtil.offsetDay(new Date(), -2));
        return getRatio(AccountDayStatEnum.REVENUE_FROM_MERCHANT_PLATFORM, accountIdList, login, brandId, xStart, xStart, yStart, yStart);
    }

    @Override
    public Double lastMonthSameRatio(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        // 获取当前日期的上月日期
        Date lastMonthDate = DateUtil.offset(new Date(), DateField.MONTH, -1);
        // 获取上月的月初
        Integer xStart = parseTime(DateUtil.beginOfMonth(lastMonthDate));
        // 获取上月的月末
        Integer xEnd = parseTime(DateUtil.endOfMonth(lastMonthDate));
        // 获取当前日期的上一年度上月的日期
        Date lastYearLastMonth = DateUtil.offset(DateUtil.offset(new Date(), DateField.YEAR, -1), DateField.MONTH, -1);
        // 获取上一年度上月的月初
        Integer yStart = parseTime(DateUtil.beginOfMonth(lastYearLastMonth));
        // 获取上一年度上月的月末
        Integer yEnd = parseTime(DateUtil.endOfMonth(lastYearLastMonth));
        return getRatio(AccountDayStatEnum.REVENUE_FROM_MERCHANT_PLATFORM, accountIdList, login, brandId, xStart, xEnd, yStart, yEnd);
    }

    @Override
    public Double lastMonthRingRatio(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        // 获取当前日期的上月日期
        Date lastMonthDate = DateUtil.offset(new Date(), DateField.MONTH, -1);
        // 获取上月的月初
        Integer xStart = parseTime(DateUtil.beginOfMonth(lastMonthDate));
        // 获取上月的月末
        Integer xEnd = parseTime(DateUtil.endOfMonth(lastMonthDate));
        // 获取上上个月的日期
        Date twoMonthsAgo = DateUtil.offset(new Date(), DateField.MONTH, -2);
        // 获取上上个月的月初
        Integer yStart = parseTime(DateUtil.beginOfMonth(twoMonthsAgo));
        // 获取上上个月的月末
        Integer yEnd = parseTime(DateUtil.endOfMonth(twoMonthsAgo));
        return getRatio(AccountDayStatEnum.REVENUE_FROM_MERCHANT_PLATFORM, accountIdList, login, brandId, xStart, xEnd, yStart, yEnd);
    }

    @Override
    public String accumulatedMileage(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        checkLogin(login);
        Long mileage = accountDayStatDao.accumulatedMileage(accountIdList, brandId, AccountDayStatEnum.ACCUMULATED_MILEAGE.getDataType());
        //大于1000千米单位设为KM
        if (mileage >= 1000 * 1000) {
            return String.format("%s 千米", mileage / 1000);
        } else {
            return String.format("%s 米", mileage);
        }
    }

    @Override
    public String reduceEmissions(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        Long count = singleResult(login, accountIdList, AccountSumStatEnum.REDUCE_EMISSIONS.getDataType(), CNT, brandId);
        if (count >= 1000 * 1000 * 10) {
            //大于10000KG的设置为吨
            return String.format("%s 吨", count / 1000 / 1000);
        } else if (count >= 1000 * 1000) {
            //大于1000KG的单位设为KG
            return String.format("%s 千克", count / 1000);
        } else {
            return String.format("%s 克", count);
        }
    }

    @Override
    public String plantTrees(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        Long count = singleResult(login, accountIdList, AccountSumStatEnum.REDUCE_EMISSIONS.getDataType(), CNT, brandId);
        //1棵树减排约为5023克二氧化碳
        return String.format("%s 棵", count / 5023);
    }

    @Override
    public List<ScreenPieVO> putOnMachinePieChart(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        List<ScreenPieVO> list = new ArrayList<>();
        Long putOnCount = singleResult(login, accountIdList, AccountSumStatEnum.NUMBER_OF_VEHICLES_LAUNCHED.getDataType(), CNT, brandId);
        Long totalCount = singleResult(login, accountIdList, AccountSumStatEnum.TOTAL_NUMBER_OF_VEHICLES.getDataType(), CNT, brandId);
        list.add(new ScreenPieVO("投放中", putOnCount));
        list.add(new ScreenPieVO("未投放", totalCount - putOnCount));
        return list;
    }

    @Override
    public List<ScreenPieVO> operationalMachinePieChart(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        List<ScreenPieVO> list = new ArrayList<>();
        Long totalCount = singleResult(login, accountIdList, AccountSumStatEnum.NUMBER_OF_VEHICLES_LAUNCHED.getDataType(), CNT, brandId);
        Long operationalCount = singleResult(login, accountIdList, AccountSumStatEnum.NUMBER_OF_OPERATIONAL_VEHICLES.getDataType(), CNT, brandId);
        list.add(new ScreenPieVO("可运营", operationalCount));
        list.add(new ScreenPieVO("不可运营", totalCount - operationalCount));
        return list;
    }

    @Override
    public List<ScreenPieVO> ridingMachinePieChart(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        List<ScreenPieVO> list = new ArrayList<>();
        list.add(new ScreenPieVO("骑行中", singleResult(login, accountIdList, AccountSumStatEnum.ON_THE_RIDE_MACHINE_COUNT.getDataType(), CNT, brandId)));
        list.add(new ScreenPieVO("待租借", singleResult(login, accountIdList, AccountSumStatEnum.MACHINE_FOR_HIRE_COUNT.getDataType(), CNT, brandId)));
        return list;
    }

    @Override
    public List<ScreenPieVO> abnormalMachinePieChart(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        List<ScreenPieVO> list = new ArrayList<>();
        list.add(new ScreenPieVO("异常车辆", singleResult(login, accountIdList, AccountSumStatEnum.NUMBER_OF_ABNORMAL_VEHICLES.getDataType(), CNT, brandId)));
        list.add(new ScreenPieVO("故障车辆", singleResult(login, accountIdList, AccountSumStatEnum.NUMBER_OF_BROKEN_DOWN_VEHICLES.getDataType(), CNT, brandId)));
        list.add(new ScreenPieVO("运维占用", singleResult(login, accountIdList, AccountSumStatEnum.NUMBER_OF_VEHICLES_OCCUPIED_BY_OPERATION_AND_MAINTENANCE.getDataType(), CNT, brandId)));
        list.add(new ScreenPieVO("缺电车辆", singleResult(login, accountIdList, AccountSumStatEnum.THE_NUMBER_OF_TROLLEY_CARS_IS_SHORT.getDataType(), CNT, brandId)));
        return list;
    }

    @Override
    public List<NameObject> machineBattery(LoginInfo login, List<Integer> accountIdList) {
        List<NameObject> nameObjects = statService.statCarBattery(login, accountIdList);
        NameObject[] arr = new NameObject[4];
        for (NameObject nameObject : nameObjects) {
            switch (nameObject.getName()) {
                case "unkonwn":
                    nameObject.setName("未知电量");
                    arr[0] = (nameObject);
                    break;
                case "normal":
                    nameObject.setName("正常电量");
                    arr[1] = (nameObject);
                    break;
                case "mid":
                    nameObject.setName("中等电量");
                    arr[2] = (nameObject);
                    break;
                case "low":
                    nameObject.setName("低电");
                    arr[3] = (nameObject);
                    break;
                default:
                    break;
            }
        }
        return Arrays.stream(arr).collect(Collectors.toList());
    }

    @Override
    public List<NameObject> machineIdle(LoginInfo login, List<Integer> accountIdList) {
        List<NameObject> nameObjects = statService.statCarIdle(login, accountIdList);
        NameObject[] arr = new NameObject[4];
        for (NameObject nameObject : nameObjects) {
            switch (nameObject.getName()) {
                case "short":
                    nameObject.setName("空闲<8h");
                    arr[0] = (nameObject);
                    break;
                case "mid":
                    nameObject.setName("空闲<24h");
                    arr[1] = (nameObject);
                    break;
                case "long":
                    nameObject.setName("空闲<48h");
                    arr[2] = (nameObject);
                    break;
                case "longlong":
                    nameObject.setName("空闲>48h");
                    arr[3] = (nameObject);
                    break;
                default:
                    break;
            }
        }
        return Arrays.stream(arr).collect(Collectors.toList());
    }

    @Override
    public List<ScreenPieVO> alarmMsg(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        DateTime endTime = new DateTime();
        DateTime startTime = DateUtil.offsetDay(endTime, -6);
        return accountDayStatDao.alarmMsg(accountIdList, parseTime(startTime), parseTime(endTime), brandId);
    }

    @Override
    public ScreenLineChartVO alarmMsgLineChart(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        ScreenLineChartVO screenLineChartVO = new ScreenLineChartVO();
        List<ScreenLineChartSeriesVO> lineChartSeriesVOS = new ArrayList<>();
        DateTime endTime = new DateTime();
        DateTime startTime = DateUtil.offsetDay(endTime, -6);
        List<String> betweenEveryDay = DateTimeUtil.getBetweenEveryDay(startTime, endTime);
        screenLineChartVO.setCategories(betweenEveryDay);
        Map<String, AccountDayStat> dataMap = new HashMap<>();
        List<AccountDayStat> accountDayStatList = accountDayStatDao.selectList(new QueryWrapper<AccountDayStat>().eq("brandId", brandId).in("accountId", accountIdList).in("dataType",
                AccountDayStatEnum.ILLEGAL_DISPLACEMENT.getDataType(),
                AccountDayStatEnum.POWER_FAILURE_ALARM.getDataType(),
                AccountDayStatEnum.FAILURE_OF_VEHICLE.getDataType()).between("dataDate", startTime, endTime));
        accountDayStatList.forEach(accountDayStat -> dataMap.put(reparseTime(accountDayStat.getDataDate()) + "_" + accountDayStat.getDataType(), accountDayStat));
        //非法位移
        List<Object> illegalDisplacement = new LinkedList<>();
        //断电告警
        List<Object> powerFailureAlarm = new LinkedList<>();
        //车辆故障
        List<Object> failureOfVehicle = new LinkedList<>();
        for (String s : betweenEveryDay) {
            AccountDayStat illegalDisplacementStat = dataMap.get(s + AccountDayStatEnum.ILLEGAL_DISPLACEMENT.getDataType());
            AccountDayStat powerFailureAlarmStat = dataMap.get(s + AccountDayStatEnum.POWER_FAILURE_ALARM.getDataType());
            AccountDayStat failureOfVehicleStat = dataMap.get(s + AccountDayStatEnum.FAILURE_OF_VEHICLE.getDataType());
            illegalDisplacement.add(illegalDisplacementStat == null ? 0 : illegalDisplacementStat.getCntTotal());
            powerFailureAlarm.add(powerFailureAlarmStat == null ? 0 : powerFailureAlarmStat.getCntTotal());
            failureOfVehicle.add(failureOfVehicleStat == null ? 0 : failureOfVehicleStat.getCntTotal());
        }
        lineChartSeriesVOS.add(new ScreenLineChartSeriesVO("非法位移", illegalDisplacement));
        lineChartSeriesVOS.add(new ScreenLineChartSeriesVO("断电告警", powerFailureAlarm));
        lineChartSeriesVOS.add(new ScreenLineChartSeriesVO("车辆故障", failureOfVehicle));
        screenLineChartVO.setSeries(lineChartSeriesVOS);
        return screenLineChartVO;
    }

    @Override
    public List<ScreenPieVO> pipelineDistribution(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        return accountSumStatDao.pipelineDistribution(accountIdList, brandId);
    }

    @Override
    public ScreenLineChartVO rankingOfOperatingIncome(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        List<Account> accountList = accountDao.getAllAccount(3);
        Map<Integer, String> map = new HashMap<>();
        accountList.forEach(a -> map.put(a.getAccountId(), a.getName()));
        List<String> categories = new LinkedList<>();
        List<ScreenLineChartSeriesVO> series = new LinkedList<>();
        ScreenLineChartSeriesVO screenLineChartSeriesVO = new ScreenLineChartSeriesVO("运营收入", null);
        List<Object> dataList = new LinkedList<>();
        List<AccountSumStat> accountSumStats = accountSumStatDao.selectList(new QueryWrapper<AccountSumStat>().eq("brandId", brandId).in("accountId", accountIdList).eq("dataType", AccountSumStatEnum.OPERATING_INCOME.getDataType()).ne("accountId", -1).orderByDesc("sumTotal"));
        if (accountIdList.contains(-1)) {
            accountSumStats.add(new AccountSumStat(null, -1, AccountSumStatEnum.OPERATING_INCOME.getDataType(), null, null, null, 0L));
        }
        Map<Integer, Double> dataMap = new HashMap<>();
        accountSumStats.forEach(a -> dataMap.put(a.getAccountId(), (double) a.getSumTotal() / 100D));
        if (accountSumStats.size() != accountIdList.size()) {
            //补全后面的为0
            for (Integer accountId : accountIdList) {
                if (dataMap.get(accountId) == null) {
                    accountSumStats.add(new AccountSumStat(null, accountId, AccountSumStatEnum.OPERATING_INCOME.getDataType(), null, null, null, 0L));
                }
            }
        }
        //取前10名
        int i = 0;
        List<Integer> idList = new ArrayList<>();
        for (AccountSumStat accountSumStat : accountSumStats) {
            idList.add(accountSumStat.getAccountId());
            if (++i >= 10) {
                break;
            }
        }
        for (Integer accountId : idList) {
            String name = map.get(accountId);
            categories.add(name == null ? "未知区域" : name);
            Double money = dataMap.get(accountId);
            dataList.add(money == null ? 0D : money);
        }
        screenLineChartSeriesVO.setData(dataList);
        series.add(screenLineChartSeriesVO);
        return new ScreenLineChartVO(categories, series);
    }

    @Override
    public Long todayNewUser(LoginInfo login, List<Integer> accountIdList, Integer dataType, int cnt, Integer brandId) {

        LambdaQueryWrapper<AccountDayStat> wrapper = Wrappers.lambdaQuery();

        wrapper.in(AccountDayStat::getAccountId, accountIdList);
        wrapper.eq(AccountDayStat::getBrandId, brandId);
        wrapper.eq(AccountDayStat::getDataType, dataType);
        wrapper.eq(AccountDayStat::getDataDate, LocalDateTime.now().format(DatePattern.PURE_DATE_FORMATTER));

        List<AccountDayStat> accountDayStatList = accountDayStatDao.selectList(wrapper);

        long count = 0;

        if (cnt == SUM) {
            count = accountDayStatList.stream().map(AccountDayStat::getSumTotal).reduce(0L, Long::sum);
        } else if (cnt == CNT) {
            count = accountDayStatList.stream().map(AccountDayStat::getCntTotal).reduce(0L, Long::sum);
        }

        return count;
    }

    @Override
    public Long totalFlowWater(LoginInfo login, List<Integer> accountIdList, Integer brandId) {
        //累计流水=商户交易+商户支出
        return singleResultToAccountDayStat(login, accountIdList, AccountDayStatEnum.REVENUE_FROM_MERCHANT_PLATFORM.getDataType(), SUM, brandId, null, null)
                +
                singleResultToAccountDayStat(login, accountIdList, AccountDayStatEnum.MERCHANT_PLATFORM_EXPENDITURE.getDataType(), SUM, brandId, null, null);
    }

    /**
     * 昨日同比增长，昨日的交易数据与上一年度同一天交易数据对比，昨日同比增长 = （昨日交易总额 - 上一年度同一天交易总额）/上一年度同一天的交易总额
     * 昨日环比增长，昨日的交易数据与前天的数据对比，昨日环比增长 = （昨日交易总额 - 前日交易总额）/ 前日交易总额
     * 上月同比增长，上月交易数据与上一年度同一月的交易数据对比，上月同比增长 = （上月交易总额 - 去年同月的交易总额）/ 去年同月交易总额
     * 上月环比增长，上月交易数据与上上月的交易数据对比，上月环比增长 = （上月交易总额 - 上上月交易总额）/ 上上月交易总额
     * 总公式  ratio = (x - y) / y
     */
    Double getRatio(AccountDayStatEnum accountDayStatEnum, List<Integer> accountIdList, LoginInfo login, Integer brandId, Integer xStartDataDate, Integer xEndDateDate, Integer yStartDataDate, Integer yEndDateDate) {
        Double ratio = null;
        //x总额
        Long xMoney = singleResultToAccountDayStat(login, accountIdList, accountDayStatEnum.getDataType(), SUM, brandId, xStartDataDate, xEndDateDate);
        //y总额
        Long yMoney = singleResultToAccountDayStat(login, accountIdList, accountDayStatEnum.getDataType(), SUM, brandId, yStartDataDate, yEndDateDate);
        if (yMoney != 0) {
            //除数不能为0
            ratio = BigDecimal.valueOf((double) (xMoney - yMoney) / (double) yMoney * 100D).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        }
        return ratio;
    }

    /**
     * 获取yyyyMMdd格式的时间
     *
     * @param date
     * @return
     */
    private Integer parseTime(Date date) {
        if (date == null) {
            return null;
        }
        return Integer.parseInt(DateUtil.format(date, "yyyyMMdd"));
    }

    public void checkLogin(LoginInfo login) {
        Account account = accountDao.getByAccountId(login.id);
        if (null == account) {
            throw new BaseException(SystemCodeEnum.NO_PRIVILEGE);
        }
    }
}
